package com.gitee.cnlongs.cnlongmq.basic.cnlongds.utils;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 系统名称：AnnotationScannerUtils
 * 模块名称：
 * 模块描述：Java类文件扫描工具类
 * 功能列表：
 * 模块作者：zouyong
 * 开发时间：2019年8月1日 下午1:42:33
 * 模块路径：com.gitee.cnlongs.cnlongmq.server.cnlongds.utils.AnnotationScannerUtils
 */
public class AnnotationScannerUtils {

	private static Logger logger = (Logger) LoggerFactory.getLogger(AnnotationScannerUtils.class);

	private static final String EXT = "class";

	/**
	 * 功能描述：根据包名获取包的URL
	 × 模块作者：ZOUYONG
	 × 开发时间：2019年8月1日 下午1:36:42
	 × 返回数据：String
	 */
	public static String getPkgPath(String pkgName) {
		String pkgDirName = pkgName.replace('.', File.separatorChar);
		URL url = Thread.currentThread().getContextClassLoader().getResource(pkgDirName);
		return url == null ? null : url.getFile();
	}

	/**
	 * 功能描述：获取指定包下所有类对象的集合
	 × 模块作者：ZOUYONG
	 × 开发时间：2019年8月1日 下午1:37:37
	 * @param pkgName 包名
	 * @param pkgPath 包路径(完整路径)
	 * @param recursive 是否递归遍历子目录
	 × 返回数据：Set<Class<?>> 类集合
	 */
	public static Set<Class<?>> scanClasses(String pkgName, String pkgPath, final boolean recursive) {
		Set<Class<?>> classesSet = new HashSet<>();
		Collection<File> allClassFile = getAllClassFile(pkgPath, recursive);
		for (File curFile : allClassFile) {
			try {
				classesSet.add(getClassObj(curFile, pkgPath, pkgName));
			} catch (Exception e) {
				logger.error("扫描指定包路径下的Class类异常：{}", e.getMessage());
			}
		}
		return classesSet;
	}

	/**
	 * 功能描述：获取指定包下包含指定注解的所有类对象的集合
	 × 模块作者：ZOUYONG
	 × 开发时间：2019年8月1日 下午1:39:09
	 * @param pkgName 包名
	 * @param pkgPath 包路径(完整路径)
	 * @param recursive 是否递归遍历子目录
	 * @param anntion 指定注解
	 */
	public static Map<Class<?>, List<Method>> 
		scanClassesByAnnotations(String pkgName, String pkgPath, boolean recursive, Class<? extends Annotation> anntion) 
	{
		Map<Class<?>, List<Method>> clsMethMap = new HashMap<Class<?>, List<Method>>();
		Collection<File> allClassFile = getAllClassFile(pkgPath, recursive);
		for (File curFile : allClassFile) {
			try {
				Class<?> curClass = getClassObj(curFile, pkgPath, pkgName);
				Method[] methods = curClass.getMethods();
				for (Method method : methods) {
					// 判断方法是否包含某个注解
					if (method.isAnnotationPresent(anntion)) {
						List<Method> list = clsMethMap.get(curClass);
						if(null == list) {
							list = new ArrayList<Method>();
						}
						list.add(method);
						clsMethMap.put(curClass, list);
					}
				}
			} catch (Exception e) {
				logger.error("扫描类中定时任务方法异常：{}", e.getMessage());
			}
		}
		return clsMethMap;
	}

	/**
	 * 功能描述：加载类
	 * @param file-Java文件
	 * @param pkgPath-包路径
	 * @param pkgName-包名称(完整路径)
	 × 模块作者：ZOUYONG
	 × 开发时间：2019年8月1日 下午1:39:45
	 × 返回数据：Class<?>
	 */
	private static Class<?> getClassObj(File file, String pkgPath, String pkgName) throws ClassNotFoundException {
		// 考虑class文件在子目录中的情况
		String absPath = file.getAbsolutePath().substring(0, file.getAbsolutePath().length() - EXT.length() - 1);
		String className = absPath.substring(pkgPath.length()).replace(File.separatorChar, '.');
		className = className.startsWith(".") ? pkgName + className : pkgName + "." + className;
		return Thread.currentThread().getContextClassLoader().loadClass(className);
	}

	/**
	 * 功能描述：遍历指定目录下所有扩展名为class的文件
	 × 模块作者：ZOUYONG
	 × 开发时间：2019年8月1日 下午1:41:01
	 * @param pkgPath 包目录
	 * @param recursive 是否递归遍历子目录
	 × 返回数据：Collection<File>
	 */
	private static Collection<File> getAllClassFile(String pkgPath, boolean recursive) {
		File fPkgDir = new File(pkgPath);
		if (!(fPkgDir.exists() && fPkgDir.isDirectory())) {
			logger.error("要查找的包文件夹子目录为空: {}", pkgPath);
			return null;
		}
		return FileUtils.listFiles(fPkgDir, new String[] { EXT }, recursive);
	}

}
